import os
import os
import re
from glob import glob
from typing import List, Dict
import cv2
import usb.core
import usb.util
from src.data.structures import BinocularImagePair


def loadImagePairs(folder: str, n_pairs: int = 10) -> list[BinocularImagePair]:
    """加载指定数量的立体图像对

    Args:
        folder (str): 图像文件夹路径
        n_pairs (int, optional): 要加载的图像对数量. Defaults to 10.

    Raises:
        FileNotFoundError: 如果未找到左图或右图

    Returns:
        list[StereoImagePair]: 立体图像对列表
    """
    pairs = []
    for i in range(1, n_pairs + 1):
        left_path = os.path.join(folder, f"calib_left_image_{i}.png")
        right_path = os.path.join(folder, f"calib_right_image_{i}.png")
        left_img = cv2.imread(left_path, cv2.IMREAD_COLOR)
        right_img = cv2.imread(right_path, cv2.IMREAD_COLOR)
        if left_img is None:
            raise FileNotFoundError(f"未找到或无法读取: {left_path}")
        if right_img is None:
            raise FileNotFoundError(f"未找到或无法读取: {right_path}")
        pairs.append(BinocularImagePair(left_img, right_img))
    return pairs


def autoLoadImagePairs(
    folder: str,
    left_pattern: str = "*left*",
    right_pattern: str = "*right*",
    ext: str = "png",
) -> List[BinocularImagePair]:
    """自动匹配并加载立体图像对

    Args:
        folder (str): 图像文件夹路径
        left_pattern (str, optional): 左图像文件名模式. Defaults to "*left*".
        right_pattern (str, optional): 右图像文件名模式. Defaults to "*right*".
        ext (str, optional): 图像文件扩展名. Defaults to "png".

    Returns:
        List[StereoImagePair]: 立体图像对列表
    """
    left_paths = sorted(
        glob(os.path.join(folder, f"{left_pattern}.{ext}"), recursive=False)
    )
    right_paths = sorted(
        glob(os.path.join(folder, f"{right_pattern}.{ext}"), recursive=False)
    )

    if not left_paths:
        print(f"[WARN] 目录下未找到符合范式 '{left_pattern}.{ext}' 的左图")
        return []
    if not right_paths:
        print(f"[WARN] 目录下未找到符合范式 '{right_pattern}.{ext}' 的右图")
        return []

    def extract_number(path: str) -> int:
        name = os.path.basename(path)
        digits = re.findall(r"\d+", name)
        if not digits:
            print(f"[WARN] 无法从文件名提取序号: {name}，跳过")
            return -1
        return int(digits[-1])

    left_dict = {extract_number(p): p for p in left_paths if extract_number(p) != -1}
    right_dict = {extract_number(p): p for p in right_paths if extract_number(p) != -1}

    common_idx = sorted(left_dict.keys() & right_dict.keys())
    if not common_idx:
        print("[WARN] 未找到序号完全匹配的左-右图对")
        return []

    pairs = []
    for idx in common_idx:
        left_path = left_dict[idx]
        right_path = right_dict[idx]
        left_img = cv2.imread(left_path)
        right_img = cv2.imread(right_path)
        if left_img is None:
            print(f"[WARN] 左图读取失败, 跳过序号 {idx}: {left_path}")
            continue
        if right_img is None:
            print(f"[WARN] 右图读取失败, 跳过序号 {idx}: {right_path}")
            continue
        pairs.append(BinocularImagePair(left_img, right_img))

    if not pairs:
        print("[WARN] 所有候选对均失败, 返回空列表")
    return pairs


def listUsbDevices() -> Dict[str, str]:
    usb_map: Dict[str, str] = {}
    for dev in usb.core.find(find_all=True):
        idx = f"{dev.bus}-{dev.address}-{getattr(dev, 'port_number', 0)}"
        name = "Unknown Device"
        if dev.iProduct:  # 仅当存在描述符索引才尝试读
            try:
                name = usb.util.get_string(dev, dev.iProduct) or "Unknown Device"
            except (usb.core.USBError, NotImplementedError, ValueError):
                # WinUSB 无 langid 或不支持字符串，直接放弃
                pass
        usb_map[idx] = name
    return usb_map
